package com.bluexmicro.android.coroutineble

import android.Manifest
import android.annotation.SuppressLint
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothManager
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.pm.PackageManager
import android.location.LocationManager
import android.os.Build
import android.provider.Settings
import android.util.Log
import androidx.core.content.ContextCompat
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlin.coroutines.cancellation.CancellationException

fun BluetoothGattCharacteristic.hasProperty(property: Int): Boolean {
    return this.properties and property > 0
}

fun BluetoothGattCharacteristic.hasProperty(properties: List<Int>): Boolean {
    return properties.all {
        this.properties and it > 0
    }
}

fun Context.checkLocation(): Boolean {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager?
        locationManager?.isLocationEnabled ?: false
    } else {
        try {
            val state =
                Settings.Secure.getInt(contentResolver, Settings.Secure.LOCATION_MODE)
            state != Settings.Secure.LOCATION_MODE_OFF
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }
}

const val FLAG_BLUETOOTH_TOGGLE = 0b00000001
const val FLAG_BLUETOOTH_SCAN = 0b00000010
const val FLAG_BLUETOOTH_CONNECT = 0b00000100
const val FLAG_BLUETOOTH_ADVERTISER = 0b00001000
const val FLAG_LOCATION_TOGGLE = 0b00010000
const val FLAG_LOCATION_PERMISSION = 0b00100000
const val STATE_READY_SCAN = 0b00110011
const val STATE_READY_CONNECT = 0b00000101
const val STATE_READY_SCAN_CONNECT = 0b00110111
const val STATE_READY_BLUETOOTH_ALL = 0b00111111

fun Context.checkBluetoothState(): Int {
    val manager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
    val bluetoothToggle = manager?.adapter?.isEnabled ?: false
    val locationToggle = checkLocation()
    val locationPermission = ContextCompat.checkSelfPermission(
        this,
        Manifest.permission.ACCESS_FINE_LOCATION
    ) == PackageManager.PERMISSION_GRANTED
    val scan: Boolean
    val connect: Boolean
    val advertiser: Boolean
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        scan = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.BLUETOOTH_SCAN
        ) == PackageManager.PERMISSION_GRANTED
        connect = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.BLUETOOTH_CONNECT
        ) == PackageManager.PERMISSION_GRANTED
        advertiser = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.BLUETOOTH_CONNECT
        ) == PackageManager.PERMISSION_GRANTED
    } else {
        scan = true
        connect = true
        advertiser = true
    }
    var state = 0
    if (bluetoothToggle) {
        state = state or FLAG_BLUETOOTH_TOGGLE
    }
    if (locationToggle) {
        state = state or FLAG_LOCATION_TOGGLE
    }
    if (locationPermission) {
        state = state or FLAG_LOCATION_PERMISSION
    }
    if (scan) {
        state = state or FLAG_BLUETOOTH_SCAN
    }
    if (connect) {
        state = state or FLAG_BLUETOOTH_CONNECT
    }
    if (advertiser) {
        state = state or FLAG_BLUETOOTH_ADVERTISER
    }
//    val intent = Intent(ACTION_BLUETOOTH_STATE)
//    intent.putExtra(PARAM_BLUETOOTH_STATE, state)
//    sendBroadcast(intent)
    Log.e("TAG", "checkBluetoothState: $state")
    return state
}

fun Context.connectReady(): Boolean {
    return checkBluetoothState() and STATE_READY_CONNECT == STATE_READY_CONNECT
}

@SuppressLint("MissingPermission")
fun Context.scanPeripheral(
    scanMode: Int = ScanSettings.SCAN_MODE_LOW_LATENCY,
    reportDelay: Long = 0
) = callbackFlow {
    val manager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
    val bluetoothAdapter = manager?.adapter
    val scanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            trySend(listOf(result))
        }

        override fun onBatchScanResults(results: MutableList<ScanResult>) {
            trySend(results)
        }

        override fun onScanFailed(errorCode: Int) {
            val exception = IllegalStateException("Scan Failed (errorCode: $errorCode)")
            cancel(CancellationException(exception.message, exception))
        }
    }
    val scanner: BluetoothLeScanner? = if (bluetoothAdapter == null) {
        val exception = IllegalStateException("Bluetooth Exception(maybe Bluetooth disable)")
        cancel(CancellationException(exception.message, exception))
        null
    } else {
        val delay = if (reportDelay > 0 && bluetoothAdapter.isOffloadedScanBatchingSupported) {
            reportDelay
        } else {
            0
        }
        val scanner = bluetoothAdapter.bluetoothLeScanner
        val scanSettings = ScanSettings.Builder()
            .setScanMode(scanMode)
            .setReportDelay(delay)
            .build()
        scanner?.startScan(null, scanSettings, scanCallback)
        scanner
    }
    awaitClose {
        scanner?.stopScan(scanCallback)
    }
}